﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using DesigneSpaceExplorerVEGA.Annotations;
using GeneticAlgorithm;
using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Series;

namespace DesigneSpaceExplorerVEGA.Models
{
    public class ApplicationModel : INotifyPropertyChanged
    {
        private int _populationCount;
        private int _threadCount;
        private int _currentCycle;
        private int _cycleCount;
        private int _currentGeneration;
        private PlotModel _xoYModel;
        private double _hypervolume;
        private PlotModel _hvModel;

        private ScatterSeries _currentGenerationSeries;
        private ScatterSeries _otherGenerationsSeries;
        private AreaSeries _hypervolumeSeries;
        private LineSeries _paretoLineSeries;
        private LineSeries _hvSeries;


        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }


        public int CurrentGeneration
        {
            get { return _currentGeneration; }
            set
            {
                _currentGeneration = value;
                OnPropertyChanged();
            }
        }

        public int CurrentCycle
        {
            get { return _currentCycle; }
            set
            {
                _currentCycle = value;
                OnPropertyChanged();
            }
        }
        public int CycleCount
        {
            get { return _cycleCount; }
            set
            {
                _cycleCount = value;
                OnPropertyChanged();
            }
        }

        public int ThreadCount
        {
            get { return _threadCount; }
            set
            {
                _threadCount = value > Environment.ProcessorCount?Environment.ProcessorCount:value;
                OnPropertyChanged();
            }
        }

        public int PopulationCount
        {
            get { return _populationCount; }
            set
            {
                _populationCount = value;
                OnPropertyChanged();
            }
        }
        public PlotModel XoYModel
        {
            get { return _xoYModel; }
            set
            {
                _xoYModel = value;
                OnPropertyChanged();
            }
        }
        public PlotModel HvModel
        {
            get { return _hvModel; }
            set
            {
                _hvModel = value;
                OnPropertyChanged();
            }
        }

        public double Hypervolume
        {
            get { return _hypervolume; }
            set
            {
                _hypervolume = value;
                OnPropertyChanged();
            }
        }

        public ApplicationModel()
        {
            XoYModel = new PlotModel();
            HvModel = new PlotModel();
            SetUpXoYModel();
            SetUpHvModel();
        }

        private void SetUpHvModel()
        {
            HvModel.Title = "Evolutia hipervolumului";
            HvModel.TitleFontSize = 14;


            var axisY = new LinearAxis();
            axisY.MajorGridlineStyle = LineStyle.Solid;
            axisY.MinorGridlineStyle = LineStyle.Dot;
            HvModel.Axes.Add(axisY);

            var axisX = new LinearAxis();
            axisX.MajorGridlineStyle = LineStyle.Solid;
            axisX.MinorGridlineStyle = LineStyle.Dot;
            axisX.Position = AxisPosition.Bottom;
            HvModel.Axes.Add(axisX);

            _hvSeries = new LineSeries();

            var zeroSeries = new ScatterSeries()
            {
                MarkerSize = 0,
                MarkerFill = OxyColors.Red,
            };
            var point = new ScatterPoint(0, 0);
            zeroSeries.Points.Add(point);
            HvModel.Series.Add(zeroSeries);
            HvModel.Series.Add(_hvSeries);
            HvModel.InvalidatePlot(true);
        }


        private void SetUpXoYModel()
        {
            XoYModel.Title = "Spatiul configuratiilor";

            SetUpXoYAxes();

            SetUpXoYLegend();

            SetUpXoYSeries();
        }

        private void SetUpXoYSeries()
        {
            _currentGenerationSeries = new ScatterSeries();
            _currentGenerationSeries.Title = "Generatie curenta";
            _currentGenerationSeries.MarkerFill = OxyColors.Red;
            _currentGenerationSeries.MarkerSize = 3;

            _otherGenerationsSeries = new ScatterSeries();
            _otherGenerationsSeries.Title = "Alte generatii";
            _otherGenerationsSeries.MarkerFill = OxyColors.Blue;
            _otherGenerationsSeries.MarkerSize = 3;

            _hypervolumeSeries = new AreaSeries();
            _hypervolumeSeries.Title = "Hipervolum";
            _hypervolumeSeries.Color = OxyColor.FromArgb(100, 100, 100, 100);     

            _paretoLineSeries = new LineSeries();
            _paretoLineSeries.Title = "Linia Pareto";
            _paretoLineSeries.MarkerFill = OxyColors.DarkOliveGreen; 

            var zeroSeries = new ScatterSeries()
            {
                MarkerSize = 0,
                MarkerFill = OxyColors.Red,
            };
            var point = new ScatterPoint(0, 0);
            zeroSeries.Points.Add(point);

            XoYModel.Series.Add(zeroSeries);
            XoYModel.Series.Add(_hypervolumeSeries);
            XoYModel.Series.Add(_paretoLineSeries);
            XoYModel.Series.Add(_otherGenerationsSeries);
            XoYModel.Series.Add(_currentGenerationSeries);

            XoYModel.InvalidatePlot(true);
        }

        private void SetUpXoYLegend()
        {
            XoYModel.IsLegendVisible = true;
            XoYModel.LegendPosition = LegendPosition.RightTop;
            XoYModel.LegendPlacement = LegendPlacement.Inside;
            XoYModel.LegendFont = "Arial";
            XoYModel.LegendFontWeight = 600;
            XoYModel.LegendBackground = OxyColor.FromArgb(50, 10, 100, 100);
            XoYModel.LegendTitle = "Legenda";
            XoYModel.LegendTitleFontSize = 16;
        }

        private void SetUpXoYAxes()
        {
            var axisY = new LinearAxis();
            axisY.MajorGridlineStyle = LineStyle.Solid;
            axisY.MinorGridlineStyle = LineStyle.Dot;
            axisY.Title = "Consum";
            XoYModel.Axes.Add(axisY);

            var axisX = new LinearAxis();
            axisX.MajorGridlineStyle = LineStyle.Solid;
            axisX.MinorGridlineStyle = LineStyle.Dot;
            axisX.Position = AxisPosition.Bottom;
            axisX.Title = "Performanță";
            XoYModel.Axes.Add(axisX);
        }

        public void UpdateHvModel(double value)
        {
            _hvSeries.Points.Add(new DataPoint(CurrentGeneration,value));
            HvModel.InvalidatePlot(true);
        }

        public void UpdateXoYModel(List<ScatterPoint> currentPoints, List<ScatterPoint> populationPoints, List<ScatterPoint> paretoPoints, ScatterPoint extremisPoint)
        {
            UpdateGlobalPopulation(populationPoints);

            UpdateCurrentPopulation(currentPoints);

            UpdateParetoAndHv(paretoPoints, extremisPoint);
   
            XoYModel.InvalidatePlot(true);
        }

        private void UpdateGlobalPopulation(List<ScatterPoint> points)
        {
            _otherGenerationsSeries.Points.Clear();
            _otherGenerationsSeries.Points.AddRange(points);
        }

        private void UpdateParetoAndHv(List<ScatterPoint> paretoPoints, ScatterPoint extremisPoint)
        {
            _paretoLineSeries.Points.Clear();
            _hypervolumeSeries.Points.Clear();
            var datapoints = new List<DataPoint>();
            foreach (var scatterPoint in paretoPoints)
            {
                datapoints.Add(new DataPoint(scatterPoint.X, scatterPoint.Y));
            }

            _paretoLineSeries.Points.AddRange(datapoints);

            _hypervolumeSeries.Points.AddRange(datapoints);
            _hypervolumeSeries.Points.Add(new DataPoint(extremisPoint.X, extremisPoint.Y));
        }

        private void UpdateCurrentPopulation(List<ScatterPoint> populationPoints)
        {
            _currentGenerationSeries.Points.Clear();
            foreach (var scatterPoint in populationPoints)
            {
                var point = new ScatterPoint(scatterPoint.X, scatterPoint.Y);
                _currentGenerationSeries.Points.Add(point);
            }

        }
    }
}
